package com.cellulam.log;

import com.cellulam.framework.core.context.AppContext;
import com.cellulam.framework.core.utils.JacksonUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE - 200)
public class LogDigestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(LogDigestInterceptor.class);

    @Around(value = "@within(com.cellulam.log.LogDigest) || " +
            "@annotation(com.cellulam.log.LogDigest)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            this.logDigest(pjp, result, System.currentTimeMillis() - start);
            return result;
        } catch (Throwable e) {
            this.logDigest(pjp, null, e, System.currentTimeMillis() - start);
            throw e;
        }
    }


    private void logDigest(ProceedingJoinPoint pjp, Object result, Throwable e, long cost) {

        MethodSignature signature = (MethodSignature) pjp.getSignature();

        LogDigest logDigest = signature.getMethod().getAnnotation(LogDigest.class);

        if (logDigest == null) {
            return;
        }

        if (e == null && logDigest.onlyException()) {
            return;
        }

        Class<?> targetCls = pjp.getTarget().getClass();

        String targetClsName = targetCls.getName();
        String targetObjectMethodName = targetClsName + "." + signature.getName();

        String message = String.format("Invoke: [%s], params: [%s], result: [%s], cost: %s, context: [%s]",
                targetObjectMethodName,
                JacksonUtils.toJson(pjp.getArgs()),
                JacksonUtils.toJson(result),
                cost,
                AppContext.getDigest());

        if (e == null) {
            logger.info(message);
        } else {
            logger.error(message, e);
        }
    }

    private void logDigest(ProceedingJoinPoint pjp, Object result, long cost) {
        logDigest(pjp, result, null, cost);
    }
}
